﻿using DotNetExtensions.AspNet.SQLites.Enums;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;

namespace DotNetExtensions.AspNet.SQLites
{
    public class SQLiteExpressionBuilder<TEntity> where TEntity : class
    {

        // Where子句中包含导航属性
        public Parameters Parameters { get; } = new Parameters();

        // 导航属性的缓存
        private readonly List<TableInfo> _entityCache = new List<TableInfo>();

        // 主表信息
        private readonly TableInfo _masterEntity;


        // 是否已经调用过Where方法
        private bool _hasInitWhere;

        // 拼接好的where子句
        private string _where;

        // 拼接好的order by子句
        private string _orderBy;
        // 构造方法
        public SQLiteExpressionBuilder()
        {
            _masterEntity = TableInfoFactory.Default.Get(typeof(TEntity));
        }



        #region Where
        public SQLiteExpressionBuilder<TEntity> Where(Expression<Func<TEntity, bool>> expr)
        {
            if (expr == null) return this;
            if (_hasInitWhere)
            {
                throw new ArgumentException("每个查询只能调用一次Where方法");
            }
            _hasInitWhere = true;

            var condition = new QueryConditionResolver<TEntity>(_masterEntity);
            var result = condition.Resolve(expr.Body);
            _where = result.Condition;
            Parameters.Add(result.Parameters);
            _entityCache.AddRange(result.NavPropertyList);
            var navProperties = result.NavPropertyList.Select(p => p.OriginName).ToList();

            return this;
        }
        #endregion

        #region OrderBy,ThenOrderBy
        public SQLiteExpressionBuilder<TEntity> OrderBy<TProperty>(Expression<Func<TEntity, TProperty>> expression,
            SQLiteOrderBy orderBy = SQLiteOrderBy.Asc)
        {

            if (expression == null) return this;
            _orderBy = " ORDER BY ";
            if (expression.Body.NodeType == ExpressionType.MemberAccess)
            {
                _orderBy += GetMemberExpression((MemberExpression)expression.Body);
                if (orderBy == SQLiteOrderBy.Desc)
                {
                    _orderBy += " DESC";
                }
            }
            if (expression.Body.NodeType == ExpressionType.Convert)
            {
                var operand = ((UnaryExpression)expression.Body).Operand;
                var member = ((MemberExpression)operand).Member;

                if (operand.Type == typeof(DateTime))
                {
                    _orderBy += $" datetime({member.Name}) ";
                }
                else
                {
                    _orderBy += member.Name;
                }
                if (orderBy == SQLiteOrderBy.Desc)
                {
                    _orderBy += " DESC";
                }
            }

            return this;
        }

        public SQLiteExpressionBuilder<TEntity> GroupBy<TProperty>(Expression<Func<TEntity, TProperty>> expression,
            SQLiteOrderBy orderBy = SQLiteOrderBy.Asc)
        {

            if (expression == null) return this;
            _orderBy = " GROUP BY ";
            if (expression.Body.NodeType == ExpressionType.MemberAccess)
            {
                _orderBy += GetMemberExpression((MemberExpression)expression.Body);
                if (orderBy == SQLiteOrderBy.Desc)
                {
                    _orderBy += " DESC";
                }
            }
            if (expression.Body.NodeType == ExpressionType.Convert)
            {
                var operand = ((UnaryExpression)expression.Body).Operand;
                var member = ((MemberExpression)operand).Member;

                if (operand.Type == typeof(DateTime))
                {
                    _orderBy += $" datetime({member.Name}) ";
                }
                else
                {
                    _orderBy += member.Name;
                }
                if (orderBy == SQLiteOrderBy.Desc)
                {
                    _orderBy += " DESC";
                }
            }

            return this;
        }

        public List<string> GetExpressionMembers<TProperty>(Expression<Func<TEntity, TProperty>> expression)
        {
            if (expression == null) return null;
            List<string> whereKeys = new List<string>();
            if (expression.Body.NodeType == ExpressionType.MemberAccess)
            {
                whereKeys.Add(GetMemberExpression((MemberExpression)expression.Body));
            }
            if (expression.Body.NodeType == ExpressionType.New)
            {
                var members = ((NewExpression)expression.Body).Members;
                whereKeys.AddRange(members.Select(c => c.Name));
            }
            return whereKeys;
        }

        public string Build()
        {
            return _where + " " + _orderBy;
        }

        public string Count(Expression<Func<TEntity, object>> expression)
        {
            var operand = ((UnaryExpression)expression.Body).Operand;
            var member = ((MemberExpression)operand).Member;

            return member.Name;
        }

        private string GetMemberExpression(MemberExpression expression)
        {
            expression.GetRootType(out var stack);
            if (stack.Count == 1)
            {
                var propName = stack.Pop();
                var prop = _masterEntity.Properties.Single(p => p.Name == propName);
                return $"{prop.Name}";
            }

            if (stack.Count == 2)
            {
                var slavePropName = stack.Pop();
                var propertyName = stack.Pop();

                var masterProp = _masterEntity.Properties.Single(p => p.Name == propertyName);
                var slaveEntity = GetIncludePropertyEntityInfo(masterProp.Type);
                var slaveProperty = slaveEntity.Properties.Single(p => p.Name == slavePropName);

                return $"[{masterProp.Name}].[{slaveProperty.Name}]";
            }

            return string.Empty;
        }
        /// 把要用到的导航属性的MyEntity缓存到一个List里，不需要每次都要到字典中获取
        private TableInfo GetIncludePropertyEntityInfo(Type type)
        {
            var entity = _entityCache.FirstOrDefault(e => e.OriginName == type.FullName);

            if (entity != null) return entity;

            entity = TableInfoFactory.Default.Get(type);
            _entityCache.Add(entity);
            return entity;
        }
        #endregion
    }
}
